/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.swan.plugin.bean;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.swan.plugin.DescriptionInterface;
import org.swan.plugin.beetl.CodeFunction;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Pojo {
    private String id;
    private Set<TemplateBean> template = new HashSet<>();
    private List<Field> fields = new ArrayList<>();
    @JsonAlias("extra")
    Map<String, Object> extraField = new HashMap<>();

    public Pojo() {
        this.id = UUID.randomUUID().toString();
    }

    public boolean addField(String fieldName, String description, String type) {
        Field field = new Field(fieldName, type, description);
        fields.add(field);
        return true;
    }

    public Pojo addExtraField(String key, Object value) {
        extraField.put(key, value);
        return this;
    }

    public Map<String, Object> getExtraField() {
        return extraField;
    }

    public Object get(String field) {
        return extraField.get(field);
    }

    public Pojo setExtraField(Map<String, Object> extraField) {
        this.extraField = extraField;
        return this;
    }

    public String getId() {
        return id;
    }

    public Pojo setId(String id) {
        this.id = id;
        return this;
    }

    public Set<TemplateBean> getTemplate() {
        return template;
    }

    public Pojo setTemplate(Set<TemplateBean> template) {
        this.template.addAll(template);
        return this;
    }

    public List<Field> getFields() {
        return fields;
    }

    public Pojo setFields(List<Field> fields) {
        this.fields.addAll(fields);
        return this;
    }


    public Pojo setQuickField(String quickField) {
        if (quickField != null) {
            for (String fieldName : quickField.split(",")) {
                fields.add(new Field().setName(fieldName).setTrainMode(true));
            }
        }
        return this;
    }

    public Pojo addTemplate(TemplateBean templateBean) {
        getTemplate().add(templateBean);
        return this;
    }

    public static class Field {
        private String name;
        @JsonAlias("train_mode")
        private boolean trainMode = true;
        private String type = "String";
        private String description;
        private String getter;
        private String setter;
        @JsonAlias("extra")
        Map<String, Object> extraField = new HashMap<>();

        private DescriptionInterface getterTemplate = DescriptionInterface.GETTER_DESCRIPTION;
        private DescriptionInterface setterTemplate = DescriptionInterface.SETTER_DESCRIPTION;

        public Field() {
        }

        public Field(String name, String type, String description) {
            this.name = name;
            this.type = type;
            this.description = description;
        }

        public Map<String, Object> getExtraField() {
            return extraField;
        }

        public Object get(String field) {
            return extraField.get(field);
        }

        public Field setExtraField(Map<String, Object> extraField) {
            this.extraField = extraField;
            return this;
        }

        public String getName() {
            return name;
        }

        public Field setName(String name) {
            this.name = name;
            return this;
        }

        public boolean isTrainMode() {
            return trainMode;
        }

        public Field setTrainMode(boolean trainMode) {
            this.trainMode = trainMode;
            return this;
        }

        public String getDescription() {
            return description == null ? name : description;
        }

        public Field setDescription(String description) {
            this.description = description;
            return this;
        }

        public String getGetter() {
            return getter == null ? getterTemplate.generate(getDescription(), this) : getter;
        }

        public Field setGetter(String getter) {
            this.getter = getter;
            return this;
        }

        public String getSetter() {
            return setter == null ? setterTemplate.generate(getDescription(), this) : setter;
        }

        public Field setSetter(String setter) {
            this.setter = setter;
            return this;
        }

        public String getType() {
            return type;
        }

        public Field setType(String type) {
            this.type = type;
            return this;
        }

        public Field setGetterTemplate(DescriptionInterface getterTemplate) {
            this.getterTemplate = getterTemplate;
            return this;
        }

        public Field setSetterTemplate(DescriptionInterface setterTemplate) {
            this.setterTemplate = setterTemplate;
            return this;
        }
    }

    public static class TemplateBean {
        public static final String CLASSPATH_PREFIX = "classpath://";
        private String url;
        @JsonAlias("package")
        private String packageName = "";
        @JsonAlias("class")
        private String className = "";
        private String description;

        private String targetUrl;

        public String getTargetUrl() {
            if (targetUrl == null) {
                CodeFunction code = new CodeFunction();
                return "java/" + getPackageName().replace('.', File.separatorChar) + File.separatorChar + code.firstUpper(code.snakeToCamel(getClassName())) + ".java";
            }
            return targetUrl;
        }

        public TemplateBean setTargetUrl(String targetUrl) {
            this.targetUrl = targetUrl;
            return this;
        }

        public String getUrl() {
            return url;
        }

        public TemplateBean setUrl(String url) {
            this.url = url;
            return this;
        }

        public String getPackageName() {
            return packageName;
        }

        public TemplateBean setPackageName(String packageName) {
            this.packageName = packageName;
            return this;
        }

        public String getClassName() {
            return className;
        }

        public TemplateBean setClassName(String className) {
            this.className = className;
            return this;
        }

        public String getDescription() {
            return description == null ? className : description;
        }

        public TemplateBean setDescription(String description) {
            this.description = description;
            return this;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            TemplateBean that = (TemplateBean) o;

            return Objects.equals(url, that.url);
        }

        @Override
        public int hashCode() {
            return url != null ? url.hashCode() : 0;
        }
    }
}
